﻿using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;

namespace Acme.BookStore2
{

    [Dependency(ReplaceServices = true)]
    [ExposeServices(
        typeof(IIdentityUserAppService),
        typeof(IdentityUserAppService),
        typeof(IExtendIdentityUserAppService),
        typeof(ExtendIdentityUserAppService))]
    public class ExtendIdentityUserAppService : IdentityUserAppService, IExtendIdentityUserAppService
    {
        private readonly OrganizationUnitManager _organizationUnitManager;
        private readonly IOrganizationUnitRepository _organizationUnitRepository;

        public ExtendIdentityUserAppService(
            OrganizationUnitManager organizationUnitManager,
            IOrganizationUnitRepository organizationUnitRepository,
            IdentityUserManager identityUserManager,
                IIdentityUserRepository userRepository,
                IIdentityRoleRepository roleRepository)
            : base(
                  identityUserManager,
                  userRepository,
                  roleRepository)
        {
            _organizationUnitManager = organizationUnitManager;
            _organizationUnitRepository = organizationUnitRepository;
        }

        public async Task<IdentityUserDto> CreateAsync(ExtendIdentityUserCreateDto input)
        {
            var identityUserDto = await base.CreateAsync(
                ObjectMapper.Map<ExtendIdentityUserCreateDto, IdentityUserCreateDto>(input)
                );
            if(!input.OrganizationUnitIds.IsNullOrEmpty())
            {
                await UserManager.SetOrganizationUnitsAsync(identityUserDto.Id, input.OrganizationUnitIds);
            }
            return identityUserDto;
        }

        public async Task<ListResultDto<OrganizationUnitDto>> GetAssignableOrganizationUnitsAsync()
        {
            var list = await _organizationUnitRepository.GetListAsync(includeDetails: true);
            return new ListResultDto<OrganizationUnitDto>(ObjectMapper.Map<List<OrganizationUnit>, List<OrganizationUnitDto>>(list));
        }

        public async Task<ListResultDto<OrganizationUnitDto>> GetOrganizationUnitsAsync(Guid id)
        {
            var ous = await UserRepository.GetOrganizationUnitsAsync(id, includeDetails: true);

            return new ListResultDto<OrganizationUnitDto>(
               ObjectMapper.Map<List<OrganizationUnit>, List<OrganizationUnitDto>>(ous));
        }

        public async Task<IdentityUserDto> UpdateAsync(Guid id, ExtendIdentityUserUpdateDto input)
        {
            var identityUserDto = await base.UpdateAsync(id, ObjectMapper.Map<ExtendIdentityUserUpdateDto, IdentityUserUpdateDto>(input));
            await UserManager.SetOrganizationUnitsAsync(id, input.OrganizationUnitIds);
            return identityUserDto;
        }

        public async Task LockUserAsync(Guid id, double? seconds)
        {
            var user = await UserRepository.GetAsync(id, includeDetails: false);
            if(seconds.HasValue)
            {
                await UserManager.SetLockoutEndDateAsync(user, DateTime.Now.AddSeconds(seconds.Value));
            }
            else
            {
                await UserManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue);
            }
        }

        public async Task UnLockUserAsync(Guid id)
        {
            var user = await UserManager.GetByIdAsync(id);
            await UserManager.SetLockoutEndDateAsync(user, DateTime.Now);
            await UserManager.ResetAccessFailedCountAsync(user);
        }

        public async Task ChangePasswordAsync(Guid id, IdentityChangePasswordInput input)
        {
            var user = await UserManager.GetByIdAsync(id);
            if (user.IsExternal)
            {
                throw new BusinessException(code: IdentityErrorCodes.ExternalUserPasswordChange);
            }
            (await UserManager.RemovePasswordAsync(user)).CheckErrors();
            (await UserManager.AddPasswordAsync(user, input.NewPassword)).CheckErrors();
            return;
        }
    }
}
